package com.heima.admin.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.heima.admin.mapper.ChannelMapper;
import com.heima.admin.mapper.SensitiveMapper;
import com.heima.admin.service.WmNewsAutoScanService;
import com.heima.aliyun.GreenImageScan;
import com.heima.aliyun.GreenTextScan;
import com.heima.exception.CustomException;
import com.heima.feigns.article.ArticleFeign;
import com.heima.feigns.wemedia.WemediaFeign;
import com.heima.model.admin.pojos.AdChannel;
import com.heima.model.admin.pojos.AdSensitive;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.utils.SensitiveWordUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class WmNewsAutoScanServiceImpl implements WmNewsAutoScanService {


    @Autowired
    private WemediaFeign wemediaFeign;

    @Override
    public void autoScanByMediaNewsId(Integer wmNewsId) {

        log.info("==========正在执行自动发布文章id={}方法", wmNewsId);

        //1.检查文章id
        if (wmNewsId == null) {
            log.error("无法审核,文章id为空");
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST, "文章idw为空");
        }

        //2.如果文章id存在,根据文章id远程获取文章信息
        WmNews wmNews = wemediaFeign.findById(wmNewsId);
        if (wmNews == null) {
            log.error("文章自动审核失败,远程查询自媒体文章失败");
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST, "对应文章不存在");
        }
        //判断状态进行发布
        //3.检查文章状态，如果文章状态为4或者8且发布时间小于当前时间,发布文章
        long now = System.currentTimeMillis();
        if ((wmNews.getStatus().equals(WmNews.Status.ADMIN_SUCCESS.getCode()) || wmNews.getStatus().equals(WmNews.Status.SUCCESS.getCode()))
                && wmNews.getPublishTime().getTime() <= now) {
            publishArticle(wmNews);
            log.info("自媒体文章被发布{}", wmNewsId);
            return;
        }

        //4.如果文章状态为1,则表示待审核状态
         if(wmNews.getStatus().equals(WmNews.Status.SUBMIT.getCode())){

             //4-1. 抽取文章内容中的文本和图片
             //抽取文章信息,图片信息 ; 如果key 为 content 则value为文本 ; 如果key 为 images 则value为List<String> 的图片地址集合
             Map<String,Object> contentAndImages = handleTextAndImages(wmNews);

             //4-2. dfa敏感词审核,如果审核失败,文章状态更改为2(自动审核失败)
             boolean isPass = handleSensitive((String)contentAndImages.get("content"),wmNews);
             if(isPass == false){
                 return;
             }log.info("==========当前文章id={},通过了DFA敏感词检测",wmNewsId);

             //4-3. aliyun文本审核,如果失败,状态更改为2,不确定待人工审,状态为改为3
             boolean isTextScan = handleTextScan((String)contentAndImages.get("content"),wmNews);
             if(! isTextScan) {
                 return;
             }
             log.info("==========当前文章id={},通过了阿里云文本检测",wmNewsId);

             //4-4. aliyun图片审核,如果失败,状态更改为2,不确定待人工审,状态为改为3
             boolean isImageScan = handleImageScan((List)contentAndImages.get("images"),wmNews);
             if(! isImageScan) {
                 return;
             }
             log.info("==========当前文章id={},通过了阿里云图片安全检测",wmNewsId);
         }

        //4-5. 如果发布时间大于当前时间,发布(更改状态; 审核通过:状态8)
        if(wmNews.getPublishTime().getTime() > now){
            updateWmNews(wmNews,(short)8,"审核通过");
            return;
        }
        //发布方法
        publishArticle(wmNews);
    }

    /**
     * 阿里云图片审核
     * @param images
     * @param wmNews
     * @return
     */

    @Autowired
    GreenImageScan greenImageScan;

    private boolean handleImageScan(List images, WmNews wmNews) {
        boolean flag = true;
        try {
            Map map = greenImageScan.imageUrlScan(images);
            String suggestion = (String)map.get("suggestion");
            if("review".equals(suggestion)){
                flag = false;
                updateWmNews(wmNews,(short)3,"图片中包含不确定因素，转为人工审核");
            }
            if("block".equals(suggestion)){
                flag = false;
                updateWmNews(wmNews,(short)2,"图片违规");
            }
        } catch (Exception e) {
            e.printStackTrace();
            flag = false;
            updateWmNews(wmNews,(short)3,"阿里云审核失败，转为人工审核");
        }
        return flag;
    }

    /**
     * 阿里云文章内容审核
     * @param content
     * @param wmNews
     * @return
     */

    @Autowired
    GreenTextScan greenTextScan;

    private boolean handleTextScan(String content, WmNews wmNews) {
        boolean flag = true;
        try {
            Map map = greenTextScan.greenTextScan(content);
            String suggestion = (String)map.get("suggestion");
            if("review".equals(suggestion)){
                flag = false;
                updateWmNews(wmNews,(short)3,"内容中包含不确定因素，转为人工审核");
            }
            if("block".equals(suggestion)){
                flag = false;
                updateWmNews(wmNews,(short)2,"文本中包含敏感词汇");
            }
        } catch (Exception e) {
            e.printStackTrace();
            flag = false;
            updateWmNews(wmNews,(short)3,"阿里云审核失败，转为人工审核");
        }
        return flag;
    }

    //注入图片oss地址前缀
    @Value("${file.oss.web-site}")
    String webSite;

    /**
     * 抽取文章中的文本内容以及图片内容
     * @param wmNews
     * @return
     */
    private Map<String,Object> handleTextAndImages(WmNews wmNews){
        //定义返回map,String为名称,Object为内容
        HashMap<String, Object> resultMap = new HashMap<>();
        //获取content内容的字符串
        String content = wmNews.getContent();
        //检查content内容不为空
        if(StringUtils.isNotBlank(content)){
            log.error("自媒体文章内容为空,审核失败");
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID,"自媒体文章内容为空");
        }
        //如果不为空,转成一个List(其中每个元素都为map)
        //其中文本内容拼成一个字符串,图片内容拼成集合
        List<Map> contentMap = JSONArray.parseArray(content,Map.class);

        //抽取所有的文本内容(Stream流,先过滤类型为文本->map方法获取内容->收集流)
        //添加拼接符号,避免敏感词误判断
        String contentStr = contentMap.stream().filter(map -> "text".equals(map.get("type")))
                .map(map -> (String) map.get("value"))
                .collect(Collectors.joining("_____"));
        contentStr = wmNews.getTitle()+"_____"+contentStr;
        //抽取所有图片内容(结果转化为listji)
        List<String> contentImages = new ArrayList<>();
                contentMap.stream().filter(map -> "image".equals(map.get("type")))
                .map(map -> (String) map.get("value"))
                .forEach(contentImages::add);
        //判断封面不为空,把集合转化为数组转化为流加上图片前缀
        if(StringUtils.isNotBlank(wmNews.getImages())){
            Arrays.stream(wmNews.getImages().split(",")).map(url->webSite+url)
                    .forEach(contentImages::add);
        }

        //对图片进行去重,降低开销
        contentImages = contentImages.stream().distinct().collect(Collectors.toList());

        //返回resultMap
        resultMap.put("content",contentStr);
        resultMap.put("image",contentImages);
        return resultMap;
    }

    @Autowired
    SensitiveMapper sensitiveMapper;

    /**
     * DFA自定义敏感词审核
     * 如果审核失败,修改文章状态为 2
     * @param content
     * @return
     */
    private boolean handleSensitive(String content,WmNews wmNews) {
        boolean flag = true;
        List<AdSensitive> adSensitives = sensitiveMapper.selectList(null);
        if(adSensitives == null && adSensitives.size()==0){
            return flag;
        }
        // 敏感词内容列表
        List<String> sensitiveList =
                adSensitives.stream().map(AdSensitive::getSensitives).collect(Collectors.toList());
        // 基于DFA算法生成DFA数据模型 (一个大map 套着一堆 小map)
        SensitiveWordUtil.initMap(sensitiveList);
        // 基于DFA算法进行敏感词检测
        Map<String, Integer> sensitiveMap = SensitiveWordUtil.matchWords(content);
        if(sensitiveMap!= null && sensitiveMap.size()>0){
            // 如果有敏感词,flag转变为false
            flag = false;
            updateWmNews(wmNews,(short)2,"文章内容中包含敏感词: "+ sensitiveMap);
        }
        return flag;
    }


    /**
     * 修改自媒体文章状态
     * @param wmNews
     * @param status
     * @param reason
     */
    private void updateWmNews(WmNews wmNews,short status,String reason){
        wmNews.setStatus(status);
        wmNews.setReason(reason);
        ResponseResult responseResult = wemediaFeign.updateWmNews(wmNews);
        if (!responseResult.getCode().equals(0)) {
            log.error("远程修改 自媒体文章状态 失败   {}", responseResult.getErrorMessage());
            throw new CustomException(AppHttpCodeEnum.SERVER_ERROR, responseResult.getErrorMessage());
        }
    }

    /**
     * 发布文章
     * @param wmNews
     */
    private void publishArticle(WmNews wmNews) {
        ResponseResult responseResult = saveApArticle(wmNews);
        if(!responseResult.getCode().equals(0)){
            log.error("文章自动审核失败,远程调用发布文章接口失败");
            throw new CustomException(AppHttpCodeEnum.SERVER_ERROR,"远程保存文章失败");
        }
        //用户关联保存成功的article文章
        wmNews.setArticleId((Long)responseResult.getData());
        //设置文章状态为9(发布成功)
        updateWmNews(wmNews,(short)9,"发布成功");
    }

    @Autowired
    ChannelMapper channelMapper;

    @Autowired
    ArticleFeign articleFeign;

    /**
     * 远程调用文章微服务
     * 主要功能给article对象封装为dto
     * 保存文章三表信息  article   article_config  article_content
     * @param wmNews
     * @return
     */
    private ResponseResult saveApArticle(WmNews wmNews) {
        //封装ArticleDto
        ArticleDto articleDto = new ArticleDto();
        BeanUtils.copyProperties(wmNews,articleDto);
            //拷贝时会将id拷贝到article的id中，手动制空
        articleDto.setId(null);
            //有可能wmNews之前已经发布过，后来下架了
        Long articleId = wmNews.getArticleId();
        if(articleId!=null){
            //设置到id字段中，远程调用时，如果有这个ID就修改。如果没有就添加
            articleDto.setId(articleId);
        }
            //封面图片布局
        articleDto.setLayout(wmNews.getType());
        articleDto.setFlag((byte) 0);
            //设置作者信息，根据wmNews关联wmUserId查询出wmUser信息
        WmUser wmUser = wemediaFeign.findWmUserById(wmNews.getUserId());
        if(wmUser == null){
            log.error("文章自动审核失败，获取关联自媒体用户失败");
            throw new CustomException(AppHttpCodeEnum.SERVER_ERROR,"获取关联自媒体用户失败");
        }
        articleDto.setAuthorName(wmUser.getName());
            //设置频道信息
        AdChannel adChannel = channelMapper.selectById(wmNews.getChannelId());
        if(adChannel == null){
            log.error("文章自动审核失败，获取关联频道信息失败");
            throw new CustomException(AppHttpCodeEnum.SERVER_ERROR,"获取关联频道信息失败");
        }
        articleDto.setChannelName(adChannel.getName());
            //远程调用文章微服务
        return articleFeign.saveArticle(articleDto);
    }

}